﻿#region Using Statements
using System;
using DirectZZT.Shared.Core;
using DirectZZT.Shared.World;
using DirectZZT.Shared.World.Objects;
using Microsoft.Xna.Framework;
#endregion

namespace DirectZZT.BoardObjects
{
    /// <summary>
    /// Represents the Conveyor ("Conveyor") board object.
    /// </summary>
    [Export("Conveyor")]
    [BoardObject(Identifier = "Conveyor", Classification = BoardObjectClassification.Terrain)]
    public sealed class ConveyorBoardObject : BoardObject
    {
        #region Fields

        private Orientation _orientation;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the ConveyorBoardObject class.
        /// </summary>
        public ConveyorBoardObject()
        {
            this.Texture = "Textures/PlayfieldObjects/Conveyor";
            // set default values
            this.Enabled = true;
            this.Visible = true;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Only DirectZZT supported.
        /// </summary>
        /// <param name="featureSet"></param>
        /// <returns></returns>
        protected override bool CanAttachScript(FeatureSet featureSet)
        {
            return featureSet == FeatureSet.DirectZZT;
        }

        /// <summary>
        /// Called when this board object has collided with another.
        /// </summary>
        /// <param name="args">The object that collided with us.</param>
        /// <returns>The resolve args, which determine what shall happen when the logic has concluded.</returns>
        public override CollisionResolveArgs OnCollide(CollisionArgs args)
        {
            return base.OnCollide(args);
        }

        /// <summary>
        /// Called when this object shall spawn.
        /// </summary>
        /// <param name="modifiers">The modifiers used to spawn this object.</param>
        public override void OnSpawn(string[] modifiers)
        {
            base.OnSpawn(modifiers);

            // check if modifiers are valid
            if (modifiers != null && modifiers.Length > 0)
            {
                Enum.TryParse<Orientation>(modifiers[0], true, out _orientation);
            }
        }

        /// <summary>
        /// Makes the conveyor spin.
        /// </summary>
        public override void OnTimerTick()
        {
            base.OnTimerTick();

            // update the orientation
            UpdateRotation();

            // move adjacent objects around
            if (this._orientation == Orientation.Clockwise)
            {
                // try to move adjacent objects
                TryMoveObject(this.Board.GetObjectAt(this.X - 1, this.Y), Direction.North);
                TryMoveObject(this.Board.GetObjectAt(this.X + 1, this.Y), Direction.South);
                TryMoveObject(this.Board.GetObjectAt(this.X, this.Y - 1), Direction.East);
                TryMoveObject(this.Board.GetObjectAt(this.X, this.Y + 1), Direction.West);
            }
            else
            {
                // try to move adjacent objects
                TryMoveObject(this.Board.GetObjectAt(this.X + 1, this.Y), Direction.North);
                TryMoveObject(this.Board.GetObjectAt(this.X - 1, this.Y), Direction.South);
                TryMoveObject(this.Board.GetObjectAt(this.X, this.Y + 1), Direction.East);
                TryMoveObject(this.Board.GetObjectAt(this.X, this.Y - 1), Direction.West);
            }
        }

        private void TryMoveObject(BoardObject obj, Direction direction)
        {
            if (obj == null)
            {
                return;
            }
            if (obj.Capabilities.HasFlag(BoardObjectCapabilities.Movable))
            {
                // try to move to new spot
                int newX = obj.X + direction.GetDeltaX();
                int newY = obj.Y + direction.GetDeltaY();
                // TODO: shall we move each object in the chain (like in BoulderBoardObject.cs)?
                obj.TryMoveTo(newX, newY);
            }
        }

        private void UpdateRotation()
        {
            if (_orientation == Orientation.Clockwise)
            {
                if (MathHelper.ToDegrees(this.TextureRotation) > 270.0f)
                {
                    this.TextureRotation = 0.0f;
                }
                this.TextureRotation += MathHelper.ToRadians(90.0f);
            }
            else
            {
                if (MathHelper.ToDegrees(this.TextureRotation) < -270.0f)
                {
                    this.TextureRotation = 0.0f;
                }
                this.TextureRotation -= MathHelper.ToRadians(90.0f);
            }
        }

        #endregion

        #region Nested types

        /// <summary>
        /// Specifies the orientation of a conveyor.
        /// </summary>
        public enum Orientation
        {
            /// <summary>
            /// The conveyor operates clockwise.
            /// </summary>
            Clockwise,
            /// <summary>
            /// The conveyor operates counter-clockwise.
            /// </summary>
            CounterClockwise,
        }

        #endregion
    }
}
